from urllib.parse import urlencode,parse_qs
from urllib.request import urlopen
import json
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer, BadData

from django.conf import settings

from meiduo_mall.utils.exceptions import logger
from .exceptions import QQAPIException
from . import constants


class OAuthQQ(object):
    """QQ登录类的工具，封装了ＱＱ登录的部分过程"""

    def __init__(self,client_id=None,redirect_uri=None,state=None,client_secret=None):
        self.client_id = client_id or settings.QQ_CLIENT_ID
        self.redirect_uri = redirect_uri or settings.QQ_REDIRECT_URI
        self.state = state or settings.QQ_STATE
        self.client_secret = client_secret or settings.QQ_CLIENT_SECRET

    def get_login_url(self):
        """提供ＱＱ登录的页面的网址
       'https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=101474184&redirect_uri=xx
        &state=user_center_info.html&scope=get_user_info'
        """
        # 准备url
        login_url = 'https://graph.qq.com/oauth2.0/authorize?'
        # 准备参数
        params = {
            'response_type':'code',
            'client_id':self.client_id,
            'redirect_uri':self.redirect_uri,
            'state':self.state,
            'scope':'get_user_info'
        }

        # 将字典转成查询字符串格式
        query_params = urlencode(params)

        # 拼接url参数
        login_url += query_params

        # 返回login_url
        return login_url

    def get_access_token(self,code):
        """
        使用code获取access-token
        :param code: authorization code
        :return: access-token
        """
        # 准备 url
        url = 'https://graph.qq.com/oauth2.0/token?'
        # 准备参数
        params = {
            'grant_type':'authorization_code',
            'client_id':self.client_id,
            'client_secret':self.client_secret,
            'code':code,
            'redirect_uri':self.redirect_uri
        }

        # 将params参数转成字典
        query_params = urlencode(params)

        # 拼接请求地址
        url += query_params

        # urlopen python发送请求   用read读取返回值
        # read 读取到的为二进制的字符串   解码不一定使用decode  二进制为decode
        try:
            response_data = urlopen(url).read()
            # 将二进制的数据解码
            response_str = response_data.decode()
            # 将字符串转成字典   (查询字符串  &符分割）
            response_dict = parse_qs(response_str)
            # 读取response-str字符串
            access_token = response_dict.get('access_token')[0]
        except Exception as e:
            logger.error(e)
            # 在封装工具类的时候，需要捕获异常，并抛出异常，千万不要解决异常，谁用谁解决
            raise QQAPIException('获取access-token失败')


        return access_token

    def get_open_id(self,access_token):
        """
        使用access-token获取openid
        :param access_token: 通过code获取到access-token
        :return: openid
        """
        # 准备请求地址
        url = 'https://graph.qq.com/oauth2.0/me?access_token=%s' % access_token
        response_str = ''
        try:
            # 美多商城向QQ服务器发送Get请求
            response_data = urlopen(url).read()
            response_str = response_data.decode()

            response_dict = json.loads(response_str[10:-4])
            # 读取openid
            open_id = response_dict.get('openid')
        except Exception as e:
            # 如果有异常，QQ服务器返回‘code=xxx&msg=xxx’
            err_data = parse_qs(response_str)
            logger.error(e)
            raise QQAPIException('code=%s msg=%s'%(err_data.get('code'),err_data.get('msg')))

        return open_id
    @staticmethod
    def generate_save_user_token(openid):
        """
        生成保存用户数据的token
        :param openid: 用户的openid
        :return: token
        """
        # 设置签名JSONWebSignatureSerializer
        serializer = Serializer(settings.SECRET_KEY,expires_in=constants.SAVE_QQ_USER_TOKEN_EXPIRES)
        data = {'openid':openid}

        token = serializer.dumps(data)
        return token.decode()

    @staticmethod
    def check_save_user_token(token):
        """
        检验保存用户数据的token
        :param token: token
        :return:  openid or None
        """
        serializer = Serializer(settings.SECRET_KEY,expires_in=constants.SAVE_QQ_USER_TOKEN_EXPIRES)
        try:
            data = serializer.loads(token)
        except BadData:
            return None
        else:
            return data.get('openid')

